/* ==================================================================   
 * Created [2009-4-27 下午11:32:55] by Jon.King 
 * ==================================================================  
 * TSS 
 * ================================================================== 
 * mailTo:jinpujun@hotmail.com
 * Copyright (c) Jon.King, 2009-2012 
 * ================================================================== 
*/

package com.jinhe.tss.core.web.rmi;

import java.io.IOException;

import org.apache.commons.httpclient.Cookie;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.log4j.Logger;

import com.jinhe.tss.core.exception.BusinessServletException;
import com.jinhe.tss.core.sso.Environment;
import com.jinhe.tss.core.sso.IOperator;
import com.jinhe.tss.core.sso.IPWDOperator;
import com.jinhe.tss.core.sso.IdentityTranslator;
import com.jinhe.tss.core.sso.IdentityTranslatorFactory;
import com.jinhe.tss.core.sso.appserver.AppServer;
import com.jinhe.tss.core.sso.context.Context;


/** 
 * <p> DominoLoginHelper.java </p> 
 * 
 * 和Domino类系统进行单点登录时的一些工具方法
 */
public class DominoLogin {
    
    private DominoLogin(){}
    
    private static DominoLogin instance;
    
    public static DominoLogin instance(){
        if(instance == null)
            instance = new DominoLogin();
        return instance;
    }
    
    private static final Logger log = Logger.getLogger(DominoLogin.class);
    
    /**
     * <p>
     * 验证用户是否登录Domino服务器，如果未登录则登陆系统；如果登录时密码为手工输入，则重新保存新密码。
     * 登录模拟如下的地址：
     * http://www.gzcz.com:8081/names.nsf?Login&Username=admin&Password=shulicq&RedirectTo=/app/testxml.nsf/success.xml
     * 如果用户名密码正确则直接转向到/app/testxml.nsf/success.xml判断是否登录成功，成功则将cookie信息返回到Response中。
     * 
     * </p>
     * @param appServer
     */
    public void loginDominoServer(AppServer appServer) throws IOException, BusinessServletException {
        if (!validateIdentity4Domino(appServer)) {
            // 用户未登录，则登录待转向的应用
            log.debug("LoginDomino:开始登陆 Domino Server (appCode:" + appServer.getCode() + ") ......");
            
            IOperator operator = getRelevantOperator(appServer);
            // 创建HttpClient对象
            HttpClient client = HttpClientHelper.instance().getHttpClient(appServer);
            PostMethod login = new PostMethod(appServer.getBaseURL() + appServer.getLoginAction()){
                // 复写PostMethod.getRequestCharSet()的编码设置为 UTF-8，以支持中文参数，如loginName为中文名
                public String getRequestCharSet() {
                    //return super.getRequestCharSet(); // 默认为ISO-8859-1
                    return "UTF-8";
                }
            };
            String loginName = operator.getLoginName();
            if (loginName == null)
                throw new BusinessServletException("对应用户没有登录名，请编辑系统（" + appServer.getCode() + "）的对应用户");
            
            login.addParameter("Username", loginName);
            String password = Context.getRequestContext().getPWD();
            boolean needSavePassword = true;
            if (password == null && operator instanceof IPWDOperator) {
                IPWDOperator passwordOperator = (IPWDOperator) operator;
                password = passwordOperator.getPassword();
                needSavePassword = false; //if判断password不是前台传进来而是operator本来就有的，不需要再次保存
            }
            password = (password == null ? "" : password);
 
            login.addParameter("Password", password);
            login.addParameter("RedirectTo", appServer.getOnlineAction());
            try {
                int statusCode = client.executeMethod(login);
                log.debug("LoginDomino:Login(" + statusCode + ")");
                
                if (statusCode == HttpStatus.SC_OK) {
                    dealWithLoginResponse4Domino(loginName, appServer, login);
                } else if ((statusCode == HttpStatus.SC_MOVED_TEMPORARILY)
                        || (statusCode == HttpStatus.SC_MOVED_PERMANENTLY)
                        || (statusCode == HttpStatus.SC_SEE_OTHER)
                        || (statusCode == HttpStatus.SC_TEMPORARY_REDIRECT)) {
                    dealWithRedirect4LoginDomino(loginName, appServer, client, login);
                } else {
                    throw new BusinessServletException(appServer.getName()
                            + "（" + appServer.getCode() + "）连接错误，错误代码：" + statusCode);
                }
                setReturnCookie2Reqeust4Domino(appServer, client);
                if (needSavePassword) {
                    log.debug("LoginDomino:Save Password(" + password + ")");
                    IdentityTranslatorFactory.getTranslator().savePassword(operator.getId(), password);
                }
            } finally {
                login.releaseConnection();
            }
            log.debug("LoginDomino:登陆完成 Domino Server");
        }
    }

    /**
     * <p>
     * 获取应用系统的用户信息
     * </p>
     * @param appServer
     * @return
     */
    private IOperator getRelevantOperator(AppServer appServer) {
        IdentityTranslator translator = IdentityTranslatorFactory.getTranslator();
        return translator.translate(Environment.getOperatorId(), appServer.getCode());
    }
    

    /**
     * <p>
     * 设置登录Domino成功后SessionId到数据请求对象中
     * </p>
     * @param appServer
     * @param client
     */
    private void setReturnCookie2Reqeust4Domino(AppServer appServer, HttpClient client) {
        // 转发返回Cookies
        Cookie[] cookies = client.getState().getCookies();
        for (int i = 0; i < cookies.length; i++) {
            String cookieName = cookies[i].getName();
            log.debug("LoginDomino:Cookie(" + cookieName + ")");
            
            if (cookieName.equals(appServer.getSessionIdName())) {
                String cookieValue = cookies[i].getValue();
                log.debug("LoginDomino: 成功取到登陆domino需要的cookie ：" + cookieName + " (" + cookieValue + ")");
                
                cookieName = appServer.getCode(); //修改cookie的名称为OA code
                javax.servlet.http.Cookie cookie = new javax.servlet.http.Cookie(cookieName, cookieValue);
                cookie.setPath(Context.getApplicationContext().getCurrentAppServer().getPath());
                Context.getRequestContext().getRequest().addCookie(cookie);
            }
        }
    }

    /**
     * <p>
     * 处理登录Domino服务器请求时Redirect的情况
     * </p>
     * @param loginName
     * @param appServer
     * @param client
     * @param login
     * @throws IOException，HttpException，BusinessServletException
     */
    private void dealWithRedirect4LoginDomino(String loginName, AppServer appServer, HttpClient client, PostMethod login)
            throws IOException, HttpException, BusinessServletException {
      
    	Header header = login.getResponseHeader("location");
        login.releaseConnection();
        if (header != null) {
            String newuri = header.getValue();
            newuri = ((newuri == null || newuri.equals("")) ? "/" : newuri);
            GetMethod redirect = new GetMethod(newuri);
            try {
                int statusCode = client.executeMethod(redirect);
                if (statusCode != HttpStatus.SC_OK) {
                    throw new BusinessServletException(appServer.getName()
                            + "（" + appServer.getCode() + "）连接错误，错误代码：" + statusCode);
                }
                dealWithLoginResponse4Domino(loginName, appServer, redirect);
            } finally {
                redirect.releaseConnection();
            }
        } else {
            throw new BusinessServletException(appServer.getName() + "（" + appServer.getCode() + "）返回错误的自动转向信息");
        }
    }

    /**
     * <p>
     * 处理登录Domino请求返回数据，判断是否登录，如果没有登录，抛出错误信息。
     * 登陆成功返回数据中有 "Success" 字样，可根据这个来判断。
     * </p>
     *
     * @param loginName
     * @param appServer
     * @param httpMethod
     * @throws IOException
     * @throws BusinessServletException
     */
    private void dealWithLoginResponse4Domino(String loginName,
            AppServer appServer, HttpMethod httpMethod) throws IOException, BusinessServletException {
       
        String result = httpMethod.getResponseBodyAsString();
        if (result.indexOf(appServer.getOnlineValidationCode()) < 0) {
            throw new BusinessServletException("用户不能登录" + appServer.getName()
                    + "（" + appServer.getCode() + "），请重新输入该系统用户（" + loginName + "）的密码", 2);
        }
    }

    /**
     * <p>
     * 验证用户是否已经登录Domino服务器
     * </p>
     * @param appServer
     * @return
     */
    private boolean validateIdentity4Domino(AppServer appServer) throws IOException, BusinessServletException {
        // 创建HttpClient对象
        HttpClient client = HttpClientHelper.instance().getHttpClient(appServer);

        GetMethod online = new GetMethod(appServer.getBaseURL() + appServer.getOnlineAction());
        try {
            int statusCode = client.executeMethod(online);
            log.debug("LoginDomino:Do Online Action(" + statusCode + ")");
            
            if (statusCode == HttpStatus.SC_OK) {
                String result = online.getResponseBodyAsString();
                if (result.indexOf(appServer.getOnlineValidationCode()) > -1) {
                    log.debug("LoginDomino:Domino Server 已经在线!");
                    return true;
                }
            } else {
                throw new BusinessServletException(appServer.getName() + "（"
                        + appServer.getCode() + "）连接错误，错误代码：" + statusCode);
            }
        } finally {
            online.releaseConnection();
        }
        
        log.debug("LoginDomino:Domino Server 当前状态为离线！");
        return false;
    }
}

